﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BL.Model;
using BL.Model.Request;
using Common;
using DAL.Model;
using DevExpress.XtraReports.UI;

namespace BL
{
    /// <summary>
    /// Controller for receipt
    /// </summary>
    /// <history>
    /// 12/05/2014 phat.vo: Create new
    /// </history>
    public class ReportController
    {
        private readonly MasterSession _masterEdm;

        #region Ctor

        public ReportController()
        {
        }

        public ReportController(MasterSession masterEdm) : this()
        {
            _masterEdm = masterEdm;
        }

        #endregion

        public class DataEnumerable : IEnumerable<DataEnumerable.Data>
        {
            private readonly List<Data> _values;

            public DataEnumerable(List<Data> source)
            {
                _values = source;
            }

            public IEnumerator<Data> GetEnumerator()
            {
                return new DataEnumerator(_values);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public class Data
            {
                public Data(string name, int value)
                {
                    Name = name;
                    Value = value;
                }

                public string Name { get; set; }
                public int Value { get; set; }
            }

            protected class DataEnumerator : IEnumerator<Data>
            {
                private readonly List<Data> _values;
                private int currentIndex;

                public DataEnumerator(List<Data> source)
                {
                    _values = source;
                    Reset();
                }

                public Data Current
                {
                    get { return _values[currentIndex]; }
                }

                public void Dispose() { }

                object IEnumerator.Current
                {
                    get { return Current; }
                }

                public bool MoveNext()
                {
                    currentIndex++;
                    return currentIndex < _values.Count;
                }

                public void Reset()
                {
                    currentIndex = -1;
                }
            }
        }

        /// <summary>
        /// Get report for liability by month
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        /// <history>
        /// 12/10/2014 phat.vo: Create new
        /// </history>
        [Obsolete("Rewrite using store proc")]
        public List<CustomerLiability> GetLiabilityReport(ReportLiabilityRequest request)
        {
            // Get current month and last month for searching liability
            var startMonth = request.FromDate;
            var startYear = request.ToDate;
            var endMonth = startMonth.AddMonths(1);

            var isAllCustomer = request.CustomerIds == null || request.CustomerIds.Contains(Constant.AllValue);
            var customerIds = isAllCustomer ? new List<int>() : request.CustomerIds;

            #region Calculatin last liability

            // Get begin liability for last liability
            var liabilitiesBeginings = (from liability in _masterEdm.LiabilitiesBeginings
                                        where (isAllCustomer || customerIds.Contains(liability.CustomerId))
                                              && liability.Year == startYear.Year
                                        select liability)
                .ToList();

            // Get all customer for result
            var result = (from customer in _masterEdm.Customers
                          where customerIds.Contains(customer.Id) || isAllCustomer
                          select new CustomerLiability
                                     {
                                         CustomerId = customer.Id,
                                         CustomerCode = customer.Code,
                                         CustomerName = customer.Name
                                     })
                .ToList();
            customerIds = result.Select(x => x.CustomerId).ToList();

            // Get total amount from start of year till report date
            var customerLiabilities = (from liability in _masterEdm.Liabilities
                                       where customerIds.Contains(liability.CustomerId)
                                             && liability.IssuedDate >= startYear
                                             && liability.IssuedDate < startMonth
                                       group liability by liability.CustomerId
                                       into g
                                       select new CustomerLiability
                                                  {
                                                      CustomerId = g.Key,
                                                      LastLiability =
                                                          g.Sum(
                                                              x =>
                                                              (x.Spend.HasValue ? x.Spend.Value : 0) -
                                                              (x.Receipt.HasValue ? x.Receipt.Value : 0))
                                                  })
                .ToList();

            #endregion

            // Get liability last month by customer
            var liabilities = (from liability in _masterEdm.Liabilities
                               where (isAllCustomer || customerIds.Contains(liability.CustomerId))
                                     && liability.IssuedDate >= startMonth && liability.IssuedDate < endMonth
                               select liability)
                .ToList();

            // Get stock out detail last month by customer
            var stockOutDetails = (from stockOutDetail in _masterEdm.StockOutDetails
                                   where (isAllCustomer ||
                                          customerIds.Contains(stockOutDetail.StockOut.CustomerId) ||
                                          customerIds.Contains((int) stockOutDetail.StockOut.Customer2Id))
                                         && stockOutDetail.StockOut.StockedDate >= startMonth
                                         && stockOutDetail.StockOut.StockedDate < endMonth
                                   select stockOutDetail)
                .OrderBy(t => t.LineNo)
                .ToList();

            // for each customer then set summary liability
            foreach (var customerReceipt in result)
            {
                #region Get Liability

                // Get liability of current month, if it doesn't exist, get last month
                // if it's neither too, set liability is 0
                var liability = liabilitiesBeginings.FirstOrDefault(x => x.CustomerId == customerReceipt.CustomerId);

                // Get last liability then set to result
                var lastLiability = customerLiabilities.FirstOrDefault(x => x.CustomerId == customerReceipt.CustomerId);

                // Set last liability
                customerReceipt.LastLiability = (liability == null ? 0 : liability.Amount)
                                                + (lastLiability == null ? 0 : lastLiability.LastLiability);

                #endregion

                var inOutMonies = new List<InOutMoney>();

                #region Get liabilities in month

                // Get stock outs then set to InOut list
                var detailLiabilities =
                    liabilities
                        .Where(t => //!(t.Accumulated.HasValue && t.Accumulated.Value) &&
                               t.CustomerId == customerReceipt.CustomerId)
                        .ToList();
                foreach (var detailLiability in detailLiabilities)
                {
                    if (detailLiability.StockOutId.HasValue)
                    {
                        var stockOuts = stockOutDetails
                            .FindAll(x => detailLiability.StockOutId == x.StockOutId
                                          && !inOutMonies.Exists(y => x.StockOut.DocumentNo.Equals(y.InOutNo)));
                        var inOutMoney = stockOuts.Select(x => new InOutMoney
                                                                   {
                                                                       LineNo = x.LineNo,
                                                                       Date = detailLiability.IssuedDate,
                                                                       Description = x.Specifical,
                                                                       Accumulated = x.Accumulated,
                                                                       InOutNo = detailLiability.StockOut.DocumentNo,
                                                                       LastLiability = 0,
                                                                       FinalLiability = 0,
                                                                       SpentAmount = x.Amount,
                                                                       Price = x.Price,
                                                                       Quantity = x.MassQuantity == 0
                                                                                      ? x.Quantity
                                                                                      : x.MassQuantity,
                                                                       Note = x.Note,
                                                                       Note2 = x.Note2
                                                                   });
                        inOutMonies.AddRange(inOutMoney);
                    }
                    else
                    {
                        inOutMonies.Add(new InOutMoney
                                            {
                                                LineNo = detailLiability.LineNo,
                                                Date = detailLiability.IssuedDate,
                                                Description = detailLiability.Receipt1.Desciption,
                                                Accumulated = detailLiability.Accumulated,
                                                InOutNo = detailLiability.Receipt1.DocumentNo,
                                                LastLiability = 0,
                                                FinalLiability = 0,
                                                ReceiptAmount = detailLiability.Receipt,
                                                DiscountAmount = detailLiability.Discount,
                                            });
                    }
                }

                // Get stock out detail of current month

                #endregion

                #region Calculate Liability

                if (!inOutMonies.Any())
                {
                    continue;
                }

                // Sort by date then No for calculating
                inOutMonies = inOutMonies
                    .OrderBy(x => x.Date)
                    .ThenBy(x => x.InOutNo)
                    .ThenBy(x => x.LineNo)
                    .ToList();

                // Insert last liability move from previous month
                inOutMonies.Insert(0, new InOutMoney
                                          {
                                              Description = "Nợ cuối mang sang",
                                              LastLiability = customerReceipt.LastLiability,
                                              FinalLiability = customerReceipt.FinalLiability,
                                          });

                var lastAmount = customerReceipt.LastLiability;

                //var inOutMoneyTruncateds = inOutMonies
                //    .Where(t => !(t.Accumulated.HasValue) ||
                //                  t.Accumulated == false)
                //    .ToArray();

                foreach (var inOutMoney in inOutMonies)
                {
                    var spentAmount = inOutMoney.SpentAmount.HasValue ? inOutMoney.SpentAmount.Value : 0;
                    var discountAmount = inOutMoney.DiscountAmount.HasValue ? inOutMoney.DiscountAmount.Value : 0;
                    var receiptAmount = inOutMoney.ReceiptAmount.HasValue ? inOutMoney.ReceiptAmount.Value : 0;
                    //inOutMoney.LastLiability = lastAmount;
                    inOutMoney.FinalLiability = lastAmount + spentAmount - discountAmount - receiptAmount;
                    lastAmount = inOutMoney.FinalLiability;
                }

                #endregion

                // Set summary liability
                customerReceipt.SpentAmount = inOutMonies
                    .Sum(x => x.SpentAmount.HasValue
                                  ? x.SpentAmount.Value
                                  : 0);
                customerReceipt.DiscountAmount = inOutMonies
                    .Sum(x => x.DiscountAmount.HasValue
                                  ? x.DiscountAmount.Value
                                  : 0);

                customerReceipt.ReceiptAmount = inOutMonies
                    .Sum(x => x.ReceiptAmount.HasValue
                                  ? x.ReceiptAmount.Value
                                  : 0);
                customerReceipt.FinalLiability = lastAmount;
                customerReceipt.InOutMonies = inOutMonies;
            }

            foreach (var t in result.Where(t => t.LastLiability +
                                                t.SpentAmount +
                                                t.ReceiptAmount +
                                                t.DiscountAmount +
                                                t.FinalLiability == 0).Reverse())
            {
                result.Remove(t);
            }

            return result;
        }

        public List<GetCustomerLiability> GetLiabilityDetails(DateTime fromDate,
            DateTime toDate,
            string customerIds)
        {
            var queryString = string.Format("GetCustomerLiability '{0}', '{1}', '{2}'",
                                            string.Concat(fromDate.Year, "-",
                                                          fromDate.Month, "-",
                                                          fromDate.Day),
                                            string.Concat(toDate.Year, "-",
                                                          toDate.Month, "-",
                                                          toDate.Day),
                                            customerIds);

            // get liability data
            var customerLiabilities = _masterEdm.Database
                .SqlQuery<GetCustomerLiability>(queryString)
                .ToList();

            // calculate remain quantity
            var remainings = customerLiabilities
                .GroupBy(k => k.CustomerId)
                .ToDictionary(k => k.Key,
                              k => k.Sum(v => v.StartLiability ?? 0) +
                                   k.Sum(v => v.Spend ?? 0) -
                                   k.Sum(v => v.Receipt ?? 0));

            // add to result set
            customerLiabilities
                .AddRange(remainings.Select(detail => new GetCustomerLiability
                                                          {
                                                              IssuedDate = null,
                                                              CustomerId = detail.Key,
                                                              Customer =
                                                                  customerLiabilities.Where(
                                                                      t => t.CustomerId == detail.Key)
                                                                  .Select(t => t.Customer)
                                                                  .First(),
                                                              Description = "Tồn cuối",
                                                              EndLiability = remainings[detail.Key]
                                                          }));
            return customerLiabilities
                .OrderBy(t => t.CustomerId)
                .ToList();
        }

        /// <summary>
        /// Get report for inventory by month
        /// </summary>
        /// <history>
        /// 12/15/2014 ninh.nguyen: Create new
        /// </history>
        public IEnumerable<GetInventoryDetail> GetInventoryDetails(DateTime fromDate,
                                                                   DateTime toDate,
                                                                   string productCode,
                                                                   string productCat,
                                                                   string customer)
        {
            var queryString = string.Format("GetInventoryDetail '{0}', '{1}', {2}, {3}, {4}",
                                            fromDate.ToString("yyyy-MM-dd"),
                                            toDate.ToString("yyyy-MM-dd"),
                                            (string.IsNullOrWhiteSpace(productCode)) ? "null" : string.Concat("'", productCode, "'"),
                                            (string.IsNullOrWhiteSpace(productCat)) ? "null" : string.Concat("'", productCat, "'"),
                                            (string.IsNullOrWhiteSpace(customer)) ? "null" : string.Concat("'", customer, "'"));

            // get liability data
            var detailInventoryItems = _masterEdm.Database
                .SqlQuery<GetInventoryDetail>(queryString)
                .ToList();
            detailInventoryItems.ForEach(t => t.ProductCode += " - " + t.Product);
            
            // calculate remain quantity
            //var remainings = detailInventoryItems
            //    .GroupBy(k => k.ProductCode)
            //    .ToDictionary(k => k.Key,
            //                  k => k.Sum(v => v.StartInventory) +
            //                       k.Sum(v => !string.IsNullOrEmpty(v.Product) ? v.StockInQty : 0) -
            //                       k.Sum(v => v.StockOutQty));

            //// add to result set
            //detailInventoryItems.AddRange(remainings.Select(detail => new GetInventoryDetail
            //                                                              {
            //                                                                  ProductCode = detail.Key,
            //                                                                  Product = "Tồn cuối",
            //                                                                  Remaining = detail.Value
            //                                                              }));
            return detailInventoryItems
                .OrderBy(t => t.ProductCode);
        }
        
        /// <summary>
        /// Gets an report object from report path.
        /// </summary>
        /// <param name="reportPath">Path of report.</param>
        /// <returns>Report object.</returns>
        /// <history>
        /// 2014.11.21 - NinhNguyen: Added.
        /// </history>
        public XtraReport GetReportFromPath(string reportPath)
        {
            // folder contains report design
            var reportFolder = Path.GetDirectoryName(reportPath);

            if (string.IsNullOrEmpty(reportFolder))
            {
                return null;
            }

            // get report file name
            var reportFileName = Path.GetFileNameWithoutExtension(reportPath);
            Type tmpType =
                System.Reflection.Assembly.GetEntryAssembly()
                    .GetType(string.Concat("UI.Reports.XtraReports.",
                                           reportFileName));
            var report = (XtraReport) Activator.CreateInstance(tmpType);
            return report;
        }
    }
}
